#ifndef __TIMERMGR_H__
#define __TIMERMGR_H__
#include <Timer.h>
namespace timer
{
    class TimerMgr final
    {
    public:
        using Ptr = std::shared_ptr<TimerMgr>;

        template <typename F, typename... TArgs>
        Timer::WeakPtr addTimer_loop(
            std::string name,
            std::chrono::seconds delayseconds,
            std::chrono::nanoseconds timeout,
            F &&callback,
            TArgs &&...args)
        {
            auto timer = std::make_shared<Timer>(
                name,
                std::chrono::steady_clock::now(),
                std::chrono::nanoseconds(timeout),
                delayseconds,
                ENUM_TIMER_TYPE::TIMER_TYPE_LOOP,
                std::bind(std::forward<F>(callback), std::forward<TArgs>(args)...));
            mtx_queue.lock();
            mTimers.push(timer);
            mtx_queue.unlock();

            timer_wakeup_state = EM_TIMER_STATE::TIMER_STATE_ADD_TIMER;
            cv.notify_one();
            return timer;
        }

        template <typename F, typename... TArgs>
        Timer::WeakPtr addTimer(
            std::string name,
            std::chrono::nanoseconds timeout,
            F &&callback,
            TArgs &&...args)
        {
            auto timer = std::make_shared<Timer>(
                name,
                std::chrono::steady_clock::now(),
                std::chrono::nanoseconds(timeout),
                std::chrono::seconds(0),
                ENUM_TIMER_TYPE::TIMER_TYPE_ONCE,
                std::bind(std::forward<F>(callback), std::forward<TArgs>(args)...));
            mtx_queue.lock();
            mTimers.push(timer);
            mtx_queue.unlock();

            timer_wakeup_state = EM_TIMER_STATE::TIMER_STATE_ADD_TIMER;
            cv.notify_one();
            return timer;
        }

        void addTimer(const Timer::Ptr &timer);

        void Close();

        ENUM_WHILE_STATE timer_run();

        void schedule();

        bool isEmpty();

        // if timer empty, return zero
        std::chrono::nanoseconds nearLeftTime();

        void clear();

    private:
        class CompareTimer
        {
        public:
            bool operator()(const Timer::Ptr &left,
                            const Timer::Ptr &right) const
            {
                const auto startDiff = left->getStartTime() - right->getStartTime();
                const auto lastDiff = left->getLastTime() - right->getLastTime();
                const auto diff = startDiff.count() + lastDiff.count();
                return diff > 0;
            }
        };

        std::priority_queue<Timer::Ptr, std::vector<Timer::Ptr>, CompareTimer> mTimers;
        std::mutex mtx;
        std::recursive_mutex mtx_queue;
        std::condition_variable cv;
        bool timer_run_ = false;
        EM_TIMER_STATE timer_wakeup_state = EM_TIMER_STATE::TIMER_STATE_EXECUTE_TIMER;
    };

    class TimerManager
    {
    public:
        TimerManager(bool autoRun = true);
        ~TimerManager();
        void Start();
        void Close();
        TimerMgr::Ptr GetTimerPtr() const;

    private:
        TimerMgr::Ptr m_timerMgr;
        std::thread m_timerThread;
        bool m_isRun;
    };
}

#endif // !__TIMERMGR_H__
